#ifndef CUDA_TOOLS_HPP
#define CUDA_TOOLS_HPP

/*
 *  系统关于CUDA的功能函数
 */
 
// #include <cuda.h>
#include <cuda_runtime.h>
#include "ilogger.hpp"

#define GPU_BLOCK_THREADS  512


#define KernelPositionBlock											\
	int position = (blockDim.x * blockIdx.x + threadIdx.x);		    \
    if (position >= (edge)) return;


// #define checkCudaDriver(call)  CUDATools::check_driver(call, #call, __LINE__, __FILE__)
#define checkCudaRuntime(call) CUDATools::check_runtime(call, #call, __LINE__, __FILE__)

#define checkCudaKernel(...)                                                                         \
    __VA_ARGS__;                                                                                     \
    do{cudaError_t cudaStatus = cudaPeekAtLastError();                                               \
    if (cudaStatus != cudaSuccess){                                                                  \
        INFOE("launch failed: %s", cudaGetErrorString(cudaStatus));                                  \
    }} while(0);


#define Assert(op)					 \
	do{                              \
		bool cond = !(!(op));        \
		if(!cond){                   \
			INFOF("Assert failed, " #op);  \
		}                                  \
	}while(false)

namespace CUDATools{
    // bool check_driver(CUresult e, const char* call, int iLine, const char *szFile);
    bool check_runtime(cudaError_t e, const char* call, int iLine, const char *szFile);
    //检查device_id对应的GPU设备是否存在
    bool check_device_id(int device_id);
    int current_device_id();

    dim3 grid_dims(int numJobs);
    dim3 block_dims(int numJobs);

    // 获取GPU计数能力
    std::string device_capability(int device_id);
    // 获取GPU名称
    std::string device_name(int device_id);
    // 获取GPU描述
    std::string device_description();

    int get_device(int device_id);

    /*
    tensor在cpu/gpu上做申请开辟内存、内存复制、内存释放等操作之前使用，属于切换设备执行的id，
    通过传入tensor的device_id来初始化AutoDevice对象，
    然后AutoDevice的构造函数使用cudaSetDevice，来设置未来的操作将在哪个id的设备执行，
    析构函数切换执行设备为默认设备，这种情况通常发生在当前设备执行id和资源操作id不同时，
    先保存当前设备执行id号到old_，然后cudaSetDevice到指定id设备进行资源操作，然后再析构返回当前设备执行id号到old_；
    */
    class AutoDevice{
    public:
        AutoDevice(int device_id = 0);
        virtual ~AutoDevice();
    
    private:
        //int old_ = -1;
    };

    class AutoCuStream{
    public:
        AutoCuStream(int device_id);
        virtual ~AutoCuStream();
        inline cudaStream_t* stream() {return &this->stream_;}

    private:
        int device_id_ = -1;
        cudaStream_t stream_;
    };
}


#endif // CUDA_TOOLS_HPP
